package com.yang.mybatis.session;

import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisEnvironment;
import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.execute.DefaultMybatisPreparedStatementBuilder;
import com.yang.mybatis.execute.DefaultMybatisResultParser;
import com.yang.mybatis.execute.IMybatisPreparedStatementBuilder;
import com.yang.mybatis.execute.IMybatisResultParser;
import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest;
import com.yang.mybatis.execute.request.MybatisResultParserRequest;
import com.yang.mybatis.proxy.MapperProxyFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;


public class DefaultMybatisSqlSession implements IMybatisSqlSession {
    private MapperProxyFactory mapperProxyFactory;
    private MybatisConfiguration mybatisConfiguration;

    private IMybatisResultParser iMybatisResultParser;

    private IMybatisPreparedStatementBuilder iMybatisPreparedStatementBuilder;

    public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory, IMybatisPreparedStatementBuilder iMybatisPreparedStatementBuilder,
                                    IMybatisResultParser iMybatisResultParser) {
        this.mapperProxyFactory = mapperProxyFactory;
        this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration();
        this.iMybatisPreparedStatementBuilder = iMybatisPreparedStatementBuilder;
        this.iMybatisResultParser = iMybatisResultParser;
    }

    @Override
    public <T> T execute(String method, Object parameter) {
        Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap();
        MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method);

        MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment();

        return new TransactionInvoke<T>() {
            @Override
            public T execute(Connection connection) throws SQLException {
                Object[] parameters = (Object[]) parameter;
                String operateType = mybatisSqlStatement.getOperateType();

                MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest = new MybatisPreparedStatementBuilderRequest();
                mybatisPreparedStatementBuilderRequest.setConnection(connection);
                mybatisPreparedStatementBuilderRequest.setMybatisSqlStatement(mybatisSqlStatement);
                mybatisPreparedStatementBuilderRequest.setParameters(parameters);
                mybatisPreparedStatementBuilderRequest.setOperateType(operateType);
                PreparedStatement preparedStatement = iMybatisPreparedStatementBuilder.buildPreparedStatement(mybatisPreparedStatementBuilderRequest);

                ResultSet resultSet = null;
                if ("select".equals(operateType)) {
                    resultSet = preparedStatement.executeQuery();
                } else {
                    preparedStatement.execute();
                }

                String mapperName = mybatisSqlStatement.getNamespace();
                MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisConfiguration.getMybatisMapperXmlConfiguration(mapperName);

                MybatisResultParserRequest mybatisResultParserRequest = new MybatisResultParserRequest();
                mybatisResultParserRequest.setResultSet(resultSet);
                mybatisResultParserRequest.setMybatisSqlStatement(mybatisSqlStatement);
                mybatisResultParserRequest.setMybatisMapperXmlConfiguration(mybatisMapperXmlConfiguration);
                mybatisResultParserRequest.setOperateType(operateType);
                T result = iMybatisResultParser.parseResult(mybatisResultParserRequest);
                if (resultSet != null) {
                    resultSet.close();
                }
                return result;
            }
        }.invoke(defaultMybatisEnvironment.getMybatisDataSource());
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return (T) mapperProxyFactory.newInstance(type, this);
    }
}
